﻿
// tehth5workView.cpp: Ctehth5workView 类的实现
#include "pch.h"
#include "framework.h"
// SHARED_HANDLERS 可以在实现预览、缩略图和搜索筛选器句柄的
// ATL 项目中进行定义，并允许与该项目共享文档代码。
#ifndef SHARED_HANDLERS
#include "tehth5work.h"
#endif
#define Round(f) int(floor(f+0.5))//四舍五入宏定义
#define  PI 3.1415926
#include"mypage.h"
#include "tehth5workDoc.h"
#include "tehth5workView.h"
#include "Vector.h"//包含矢量类
#include "Face.h"//包含面类
#include "Lighting.h"//包含光照类
#include"ZBuffer.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#endif


// Ctehth5workView

IMPLEMENT_DYNCREATE(Ctehth5workView, CView)

BEGIN_MESSAGE_MAP(Ctehth5workView, CView)
	// 标准打印命令
	ON_COMMAND(ID_FILE_PRINT, &CView::OnFilePrint)
	ON_COMMAND(ID_FILE_PRINT_DIRECT, &CView::OnFilePrint)
	ON_COMMAND(ID_FILE_PRINT_PREVIEW, &Ctehth5workView::OnFilePrintPreview)
	ON_WM_CONTEXTMENU()
	ON_WM_RBUTTONUP()
	ON_COMMAND(ID_32773, &Ctehth5workView::On32773)
	ON_COMMAND(ID_32774, &Ctehth5workView::On32774)
	ON_COMMAND(ID_32775, &Ctehth5workView::On32775)
	ON_COMMAND(ID_32776, &Ctehth5workView::On32776)
	ON_COMMAND(ID_32777, &Ctehth5workView::On32777)
	ON_COMMAND(ID_32778, &Ctehth5workView::On32778)
	ON_COMMAND(ID_32779, &Ctehth5workView::On32779)
	ON_COMMAND(ID_32780, &Ctehth5workView::On32780)
	ON_WM_LBUTTONDOWN()
	ON_WM_KEYDOWN()
	ON_COMMAND(ID_32792, &Ctehth5workView::On32792)
	ON_COMMAND(ID_32793, &Ctehth5workView::On32793)
	ON_COMMAND(ID_32794, &Ctehth5workView::On32794)
	ON_COMMAND(ID_32795, &Ctehth5workView::On32795)
	ON_COMMAND(ID_32796, &Ctehth5workView::On32796)
	ON_COMMAND(ID_32797, &Ctehth5workView::On32797)
	ON_COMMAND(ID_32798, &Ctehth5workView::On32798)
	ON_COMMAND(ID_32799, &Ctehth5workView::On32799)
	ON_COMMAND(ID_32786, &Ctehth5workView::On32786)
	ON_COMMAND(ID_32788, &Ctehth5workView::On32788)
	ON_COMMAND(ID_32790, &Ctehth5workView::On32790)
	ON_COMMAND(ID_32787, &Ctehth5workView::On32787)
	ON_WM_TIMER()
	ON_COMMAND(ID_32789, &Ctehth5workView::On32789)
	ON_COMMAND(ID_32791, &Ctehth5workView::On32791)
END_MESSAGE_MAP()

// Ctehth5workView 构造/析构

Ctehth5workView::Ctehth5workView() noexcept
{
	// TODO: 在此处添加构造代码
	m_bRotateX = false;
	m_bRotateY = false;
	m_bRotateZ = false;//三个表示三个光源的旋转变量
	R = 1000.0, d = 800.0, Phi = 90.0, Theta = 0.0;//视点位于正前方
	Near = 200.0, Far = 1200.0;//近剪切面与远剪切面		
	Alpha = 0.0; Beta = 0.0;
	LightNum = 3;//光源个数
	pLight = new CLighting(LightNum);//一维光源动态数组
	pLight->Light[0].SetPosition(800, 0, 0);//设置第一个光源位置坐标
	x1 = 800; y1 = z1 = 0;
	pLight->Light[1].SetPosition(0, 800, 0);//设置第一个光源位置坐标
	y2 = 800; x2 = z2 = 0;
	pLight->Light[2].SetPosition(0, 0, 800);//设置第一个光源位置坐标
	x3 = y3 = 0; z3 = 800;
	for (int i = 0; i < LightNum; i++)
	{
		pLight->Light[i].L_Diffuse = CRGB(1.0, 1.0, 1.0); //光源的漫反射颜色	
		pLight->Light[i].L_Specular = CRGB(1.0, 1.0, 1.0);//光源镜面高光颜色
		pLight->Light[i].L_C0 = 1.0;//常数衰减系数
		pLight->Light[i].L_C1 = 0.0000001;//线性衰减系数
		pLight->Light[i].L_C2 = 0.00000001;//二次衰减系数
		pLight->Light[i].L_OnOff = TRUE;//光源开启	
	}
	pMaterial = new CMaterial;//一维材质动态数组
	pMaterial->SetAmbient(CRGB(0.175, 0.012, 0.012));//材质对环境光光的反射率
	pMaterial->SetDiffuse(CRGB(0.614, 0.041, 0.041));//材质对漫反射光的反射率
	pMaterial->SetSpecular(CRGB(1.0, 1.0, 1.0));//材质对镜面反射光的反射率
	pMaterial->SetEmit(CRGB(0.0, 0.0, 0.0));//材质自身发散的颜色
	pMaterial->M_n = 30.0;//高光指数

}

Ctehth5workView::~Ctehth5workView()
{
}

BOOL Ctehth5workView::PreCreateWindow(CREATESTRUCT& cs)
{
	// TODO: 在此处通过修改
	//  CREATESTRUCT cs 来修改窗口类或样式

	return CView::PreCreateWindow(cs);
}

// Ctehth5workView 绘图

void Ctehth5workView::OnDraw(CDC* /*pDC*/)
{
	Ctehth5workDoc* pDoc = GetDocument();
	ASSERT_VALID(pDoc);
	if (!pDoc)
		return;

	// TODO: 在此处为本机数据添加绘制代码
	ReadVertex();
	ReadFace();
	InitParameter();
	DoubleBuffer();
}


// Ctehth5workView 打印


void Ctehth5workView::OnFilePrintPreview()
{
#ifndef SHARED_HANDLERS
	AFXPrintPreview(this);
#endif
}

BOOL Ctehth5workView::OnPreparePrinting(CPrintInfo* pInfo)
{
	// 默认准备
	return DoPreparePrinting(pInfo);
}

void Ctehth5workView::OnBeginPrinting(CDC* /*pDC*/, CPrintInfo* /*pInfo*/)
{
	// TODO: 添加额外的打印前进行的初始化过程
}

void Ctehth5workView::OnEndPrinting(CDC* /*pDC*/, CPrintInfo* /*pInfo*/)
{
	// TODO: 添加打印后进行的清理过程
}

void Ctehth5workView::OnRButtonUp(UINT /* nFlags */, CPoint point)
{
	R = R - 100;
	DoubleBuffer();
	DoubleBuffer();
	
	OnContextMenu(this, point);
}

void Ctehth5workView::OnContextMenu(CWnd* /* pWnd */, CPoint point)
{
#ifndef SHARED_HANDLERS
	theApp.GetContextMenuManager()->ShowPopupMenu(IDR_POPUP_EDIT, point.x, point.y, this, TRUE);
#endif
}


// Ctehth5workView 诊断

#ifdef _DEBUG
void Ctehth5workView::AssertValid() const
{
	CView::AssertValid();
}

void Ctehth5workView::Dump(CDumpContext& dc) const
{
	CView::Dump(dc);
}

Ctehth5workDoc* Ctehth5workView::GetDocument() const // 非调试版本是内联的
{
	ASSERT(m_pDocument->IsKindOf(RUNTIME_CLASS(Ctehth5workDoc)));
	return (Ctehth5workDoc*)m_pDocument;
}
#endif //_DEBUG


// Ctehth5workView 消息处理程序


void Ctehth5workView::ReadVertex() {
	int gAlpha = 4, gBeta = 4;//面片夹角
	N1 = 180 / gAlpha, N2 = 360 / gBeta;//N1为纬度区域,N2为经度区域
	V = new CP3[(N1 - 1) * N2 + 2];//V为球的顶点
	//纬度方向除南北极点外有"N1－1"个点，"2"代表南北极两个点
	double gAlpha1, gBeta1, r = 300;//r为球体半径
	//计算北极点坐标
	V[0].x = 0, V[0].y = r, V[0].z = 0;
	//按行循环计算球体上的点坐标
	for (int i = 0; i < N1 - 1; i++)
	{
		gAlpha1 = (i + 1) * gAlpha * PI / 180;
		for (int j = 0; j < N2; j++)
		{
			gBeta1 = j * gBeta * PI / 180;
			V[i * N2 + j + 1].x = r * sin(gAlpha1) * sin(gBeta1);
			V[i * N2 + j + 1].y = r * cos(gAlpha1);
			V[i * N2 + j + 1].z = r * sin(gAlpha1) * cos(gBeta1);
		}
	}
	//计算南极点坐标
	V[(N1 - 1) * N2 + 1].x = 0, V[(N1 - 1) * N2 + 1].y = -r, V[(N1 - 1) * N2 + 1].z = 0;
}


void Ctehth5workView::ReadFace()
{
	// TODO: 在此处添加实现代码.
	//面的边数、面的顶点编号和面的颜色
	F = new CFace * [N1];//设置行
	for (int n = 0; n < N1; n++)
		F[n] = new CFace[N2];//设置列
	for (int j = 0; j < N2; j++)//构造北极三角形面片
	{
		int tempj = j + 1;
		if (tempj == N2) tempj = 0;//面片的首尾连接
		int NorthIndex[3];//北极三角形面片索引号数组
		NorthIndex[0] = 0;
		NorthIndex[1] = j + 1;
		NorthIndex[2] = tempj + 1;
		F[0][j].SetNum(3);
		for (int k = 0; k < F[0][j].vN; k++)
			F[0][j].vI[k] = NorthIndex[k];
	}
	for (int i = 1; i < N1 - 1; i++)//构造球面四边形面片
	{
		for (int j = 0; j < N2; j++)
		{
			int tempi = i + 1;
			int tempj = j + 1;
			if (tempj == N2) tempj = 0;
			int BodyIndex[4];//球面四边形面片索引号数组
			BodyIndex[0] = (i - 1) * N2 + j + 1;
			BodyIndex[1] = (tempi - 1) * N2 + j + 1;
			BodyIndex[2] = (tempi - 1) * N2 + tempj + 1;
			BodyIndex[3] = (i - 1) * N2 + tempj + 1;
			F[i][j].SetNum(4);
			for (int k = 0; k < F[i][j].vN; k++)
				F[i][j].vI[k] = BodyIndex[k];
		}
	}
	for (int j = 0; j < N2; j++)//构造南极三角形面片
	{
		int tempj = j + 1;
		if (tempj == N2) tempj = 0;
		int SouthIndex[3];//南极三角形面片索引号数组
		SouthIndex[0] = (N1 - 2) * N2 + j + 1;
		SouthIndex[1] = (N1 - 1) * N2 + 1;
		SouthIndex[2] = (N1 - 2) * N2 + tempj + 1;
		F[N1 - 1][j].SetNum(3);
		for (int k = 0; k < F[N1 - 1][j].vN; k++)
			F[N1 - 1][j].vI[k] = SouthIndex[k];
	}
}


void Ctehth5workView::InitParameter()
{
	// TODO: 在此处添加实现代码.
	k[1] = sin(PI * Theta / 180);
	k[2] = sin(PI * Phi / 180);
	k[3] = cos(PI * Theta / 180);
	k[4] = cos(PI * Phi / 180);
	k[5] = k[2] * k[3];
	k[6] = k[2] * k[1];
	k[7] = k[4] * k[3];
	k[8] = k[4] * k[1];
	ViewPoint.x = R * k[6];
	ViewPoint.y = R * k[4];
	ViewPoint.z = R * k[5];
}


void Ctehth5workView::PerProject(CP3 P)
{
	// TODO: 在此处添加实现代码.

	CP3 ViewP;
	ViewP.x = P.x * k[3] - P.z * k[1];//观察坐标系三维坐标
	ViewP.y = -P.x * k[8] + P.y * k[2] - P.z * k[7];
	ViewP.z = -P.x * k[6] - P.y * k[4] - P.z * k[5] + R;
	ViewP.c = P.c;
	ScreenP.x = d * ViewP.x / ViewP.z;//屏幕坐标系三维坐标
	ScreenP.y = Round(d * ViewP.y / ViewP.z);
	ScreenP.z = Far * (1 - Near / ViewP.z) / (Far - Near);
	ScreenP.c = ViewP.c;
}


void Ctehth5workView::DoubleBuffer()
{
	// TODO: 在此处添加实现代码.
	CRect Rect;
	GetClientRect(&Rect);
	CDC* pDC = GetDC();

	// 设置 pDC 的坐标系
	pDC->SetMapMode(MM_ANISOTROPIC);
	pDC->SetWindowExt(Rect.Width(), Rect.Height());
	pDC->SetViewportExt(Rect.Width(), -Rect.Height());
	pDC->SetViewportOrg(Rect.Width() / 2, Rect.Height() / 2);

	// 创建内存 DC 和加载位图
	CDC MemDC;
	MemDC.CreateCompatibleDC(pDC);

	CBitmap NewBitmap, * OldBitmap;
	NewBitmap.CreateCompatibleBitmap(pDC, Rect.Width(), Rect.Height());
	OldBitmap = MemDC.SelectObject(&NewBitmap);

	// 设置 MemDC 坐标系
	MemDC.SetMapMode(MM_ANISOTROPIC);
	MemDC.SetWindowExt(Rect.Width(), Rect.Height());
	MemDC.SetViewportExt(Rect.Width(), -Rect.Height());
	MemDC.SetViewportOrg(Rect.Width() / 2, Rect.Height() / 2);

	// 填充背景为白色
	MemDC.FillSolidRect(-Rect.Width() / 2, -Rect.Height() / 2, Rect.Width(), Rect.Height(), RGB(255, 255, 255));

	// 执行其他绘制操作
	DrawObject(&MemDC);

	// 将内存 DC 的内容绘制到屏幕
	pDC->BitBlt(-Rect.Width() / 2, -Rect.Height() / 2, Rect.Width(), Rect.Height(), &MemDC, -Rect.Width() / 2, -Rect.Height() / 2, SRCCOPY);

	// 清理资源
	MemDC.SelectObject(OldBitmap);
	NewBitmap.DeleteObject();
	ReleaseDC(pDC);
}


void Ctehth5workView::DrawObject(CDC* pDC)
{
	// TODO: 在此处添加实现代码.
	CZBuffer* zbuf = new CZBuffer;//申请内存
	zbuf->InitDeepBuffer(800, 800, 1000);//初始化深度缓冲器
	CPi3 Point3[3];//南北极顶点数组
	CVector Normal3[3]; //南北极顶点的法矢量数组
	CPi3 Point4[4];//球体顶点数组
	CVector Normal4[4]; //球体顶点的法矢量数组
	for (int i = 0; i < N1; i++)
	{
		for (int j = 0; j < N2; j++)
		{
			CVector ViewVector(V[F[i][j].vI[0]], ViewPoint);//面的视矢量
			ViewVector = ViewVector.Normalize();//单位化视矢量
			F[i][j].SetFaceNormal(V[F[i][j].vI[0]], V[F[i][j].vI[1]], V[F[i][j].vI[2]]);
			F[i][j].fNormal.Normalize();//单位化法矢量
			if (Dot(ViewVector, F[i][j].fNormal) >= 0)//背面剔除
			{
				if (3 == F[i][j].vN)
				{
					for (int m = 0; m < F[i][j].vN; m++)
					{
						PerProject(V[F[i][j].vI[m]]);
						Point3[m] = ScreenP;
						Normal3[m] = CVector(V[F[i][j].vI[m]]);
					}
					zbuf->SetPoint(Point3, Normal3, 3);//初始化
					zbuf->CreateBucket();//创建桶表
					zbuf->CreateEdge();//创建边表
					zbuf->Phong(pDC, ViewPoint, pLight, pMaterial);//颜色渐变填充三角形
					zbuf->ClearMemory();
				}
				else
				{
					for (int m = 0; m < F[i][j].vN; m++)
					{
						PerProject(V[F[i][j].vI[m]]);
						Point4[m] = ScreenP;
						Normal4[m] = CVector(V[F[i][j].vI[m]]);
					}
					zbuf->SetPoint(Point4, Normal4, 4);//设置顶点
					zbuf->CreateBucket();//创建桶表
					zbuf->CreateEdge();//创建边表
					zbuf->Phong(pDC, ViewPoint, pLight, pMaterial);//填充四边形
					zbuf->ClearMemory();
				}
			}
		}
	}
	delete zbuf;
}




void Ctehth5workView::On32773()
{
	// TODO: 在此添加命令处理程序代码
	pLight->Light[0].SetSpecular(CRGB(1.0, 0.0, 0.0));
	InitParameter();
	DoubleBuffer();

}


void Ctehth5workView::On32774()
{
	// TODO: 在此添加命令处理程序代码、
	pLight->Light[0].SetSpecular(CRGB(0.0, 1.0, 0.0));
	InitParameter();
	DoubleBuffer();
}


void Ctehth5workView::On32775()
{
	// TODO: 在此添加命令处理程序代码
	pLight->Light[0].SetSpecular(CRGB(0.0, 0.0, 1.0));
	InitParameter();
	DoubleBuffer();
}


void Ctehth5workView::On32776()
{
	// TODO: 在此添加命令处理程序代码
	pLight->Light[0].SetSpecular(CRGB(1.0, 1.0, 1.0));
	InitParameter();
	DoubleBuffer();
}


void Ctehth5workView::On32777()
{
	// TODO: 在此添加命令处理程序代码
	pMaterial->SetDiffuse(CRGB(1.0, 0.0, 0.0));
	InitParameter();
	DoubleBuffer();
}


void Ctehth5workView::On32778()
{
	// TODO: 在此添加命令处理程序代码
	pMaterial->SetDiffuse(CRGB(0.0, 1.0, 0.0));
	InitParameter();
	DoubleBuffer();
}


void Ctehth5workView::On32779()
{
	// TODO: 在此添加命令处理程序代码
	pMaterial->SetDiffuse(CRGB(0.0, 0.0, 1.0));
	InitParameter();
	DoubleBuffer();
}


void Ctehth5workView::On32780()
{
	// TODO: 在此添加命令处理程序代码
	pMaterial->SetDiffuse(CRGB(1.0, 1.0, 1.0));
	InitParameter();
	DoubleBuffer();
}


void Ctehth5workView::OnLButtonDown(UINT nFlags, CPoint point)
{
	// TODO: 在此添加消息处理程序代码和/或调用默认值
	R = R + 100;
	DoubleBuffer();
	DoubleBuffer();
	CView::OnLButtonDown(nFlags, point);
}


void Ctehth5workView::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
{
	// TODO: 在此添加消息处理程序代码和/或调用默认值
	switch (nChar)
	{
	case VK_UP:
		Phi -= 5;
		break;
	case VK_DOWN:
		Phi += 5;
		break;
	case VK_LEFT:
		Theta -= 5;
		break;
	case VK_RIGHT:
		Theta += 5;
		break;
	default:
		break;
	}
	pLight->Light[0].SetGlobal(R, Phi, Theta);
	pLight->Light[1].SetGlobal(R, Phi, Theta);
	pLight->Light[2].SetGlobal(R, Phi, Theta);
	InitParameter();
	DoubleBuffer();
	CView::OnKeyDown(nChar, nRepCnt, nFlags);
}

void Ctehth5workView::On32792()
{
	// TODO: 在此添加命令处理程序代码
	pLight->Light[1].SetSpecular(CRGB(1.0, 0.0, 0.0));
	DoubleBuffer();

}


void Ctehth5workView::On32793()
{
	// TODO: 在此添加命令处理程序代码、
	pLight->Light[1].SetSpecular(CRGB(0.0, 1.0, 0.0));
	DoubleBuffer();
}


void Ctehth5workView::On32794()
{
	// TODO: 在此添加命令处理程序代码
	pLight->Light[1].SetSpecular(CRGB(0.0, 0.0, 1.0));
	DoubleBuffer();
}


void Ctehth5workView::On32795()
{
	// TODO: 在此添加命令处理程序代码
	pLight->Light[1].SetSpecular(CRGB(1.0, 1.0, 1.0));
	DoubleBuffer();
}


void Ctehth5workView::On32796()
{
	// TODO: 在此添加命令处理程序代码
	pLight->Light[2].SetSpecular(CRGB(1.0, 0.0, 0.0));
	DoubleBuffer();

}


void Ctehth5workView::On32797()
{
	// TODO: 在此添加命令处理程序代码、
	pLight->Light[2].SetSpecular(CRGB(0.0, 1.0, 0.0));
	DoubleBuffer();
}


void Ctehth5workView::On32798()
{
	// TODO: 在此添加命令处理程序代码
	pLight->Light[2].SetSpecular(CRGB(0.0, 0.0, 1.0));
	DoubleBuffer();
}


void Ctehth5workView::On32799()
{
	// TODO: 在此添加命令处理程序代码
	pLight->Light[2].SetSpecular(CRGB(1.0, 1.0, 1.0));
	DoubleBuffer();
}

void Ctehth5workView::On32786()
{
	// TODO: 在此添加命令处理程序代码
	mypage  dlg;
	if (dlg.DoModal() == IDOK) {
		x1 = dlg.x;
		y1 = dlg.y;
		z1 = dlg.z;
		pLight->Light[0].SetPosition(dlg.x, dlg.y, dlg.z);//设置第一个光源位置坐标
		DoubleBuffer();
	}
}


void Ctehth5workView::On32788()
{
	// TODO: 在此添加命令处理程序代码
	mypage  dlg;
	if (dlg.DoModal() == IDOK) {
		x2 = dlg.x;
		y2 = dlg.y;
		z2 = dlg.z;
		pLight->Light[1].SetPosition(dlg.x, dlg.y, dlg.z);//设置第一个光源位置坐标
		DoubleBuffer();
	}
}


void Ctehth5workView::On32790()
{
	// TODO: 在此添加命令处理程序代码
	mypage  dlg;
	if (dlg.DoModal() == IDOK) {
		pLight->Light[2].SetPosition(dlg.x, dlg.y, dlg.z);//设置第一个光源位置坐标
		DoubleBuffer();
	}
}


void Ctehth5workView::On32787()
{
	// TODO: 在此添加命令处理程序代码
	m_bRotateY = !m_bRotateY;
	if (m_bRotateY) {
		SetTimer(1, 50, NULL);
	}
	else
		KillTimer(1);
}

void Ctehth5workView::On32789()
{
	// TODO: 在此添加命令处理程序代码
	m_bRotateZ = !m_bRotateZ;
	if (m_bRotateZ) {
		SetTimer(2, 50, NULL);
	}
	else
		KillTimer(2);
}




void Ctehth5workView::On32791()
{
	// TODO: 在此添加命令处理程序代码
	m_bRotateX = !m_bRotateX;
	if (m_bRotateX) {
		SetTimer(3, 50, NULL);
	}
	else
		KillTimer(3);
}
void Ctehth5workView::OnTimer(UINT_PTR nIDEvent)
{
	// 旋转角度（弧度）
	 angle =3.1415926/4;

	// 临时变量存储新坐标
	double newX, newY, newZ;

	// 根据 nIDEvent 选择需要旋转的光源
	switch (nIDEvent) {
	case 1: // 绕 Y 轴旋转第一个光源
		newX = x1 * cos(angle) + z1 * sin(angle);
		newY = y1; // Y 坐标不变
		newZ = -x1 * sin(angle) + z1 * cos(angle);
		x1 = newX; y1 = newY; z1 = newZ;
		pLight->Light[0].SetPosition(x1, y1, z1);
		break;

	case 2: // 绕 Z 轴旋转第二个光源
		newX = x2 * cos(angle) - y2 * sin(angle);
		newY = x2 * sin(angle) + y2 * cos(angle);
		newZ = z2; // Z 坐标不变
		x2 = newX; y2 = newY; z2 = newZ;
		pLight->Light[1].SetPosition(x2, y2, z2);
		break;

	case 3: // 绕 X 轴旋转第三个光源
		newX = x3; // X 坐标不变
		newY = y3 * cos(angle) - z3 * sin(angle);
		newZ = y3 * sin(angle) + z3 * cos(angle);
		x3 = newX; y3 = newY; z3 = newZ;
		pLight->Light[2].SetPosition(x3, y3, z3);
		break;

	default:
		break;
	}


	// 双缓冲刷新
	DoubleBuffer();

	// 调用父类的定时器处理
	CView::OnTimer(nIDEvent);
}
